<?php
/**
 * @copyright © 2022 by 技术先锋 All rights reserved
 * @created   by PhpStorm.
 * @author    starsphp
 * @date      2023/3/29
 * @time      23:44
 */
declare (strict_types=1);

namespace utils\workerman;

use think\App;
use think\exception\FileException;
use think\File;
use think\file\UploadedFile;

class Request extends \think\Request
{
    /**
     * 是否允许上传文件
     * 是否允许 HTTP 文件上传。参阅 upload_max_filesize、upload_tmp_dir 和 post_max_size 指令。
     * file_uploads bool
     *
     * @var string
     */
    protected static mixed $_fileUploads = true;
    
    /**
     * 文件上传临时目录
     * 上传文件时用来存放文件的临时目录。必须允许运行 PHP 的任何用户写入。如果未指定，PHP 将使用系统默认值。
     * 如果此处指定的目录不可写，PHP 回退到系统默认临时目录。如果打开 open_basedir，然后允许系统临时目录才可以成功上传。
     * Upload tmp dir.
     *
     * @var string
     */
    protected static string $_uploadTmpDir = '';
    
    /**
     * 文件上传最大值
     * 上传文件的最大大小。
     * post_max_size 必须大于此值。
     * 当使用 int 时, 其值以字节来衡量。还可以使用在 FAQ 中描述的速记符。
     * upload_max_filesize.
     *
     * @var string
     */
    protected static string $_uploadMaxFilesize = '';
    
    /**
     * 允许同时上传的最大文件数。提交时留空的上传字段不计入此限制。
     * max_file_uploads.
     *
     * @var string
     */
    protected static int $_maxFileUploads = 0;
    
    public static function __make(App $app)
    {
        $request = new static();
        
        if (function_exists('apache_request_headers') && $result = apache_request_headers()) {
            $header = $result;
        } else {
            $header = [];
            $server = $_SERVER;
            foreach ($server as $key => $val) {
                if (0 === strpos($key, 'HTTP_')) {
                    $key          = str_replace('_', '-', strtolower(substr($key, 5)));
                    $header[$key] = $val;
                }
            }
            if (isset($server['CONTENT_TYPE'])) {
                $header['content-type'] = $server['CONTENT_TYPE'];
            }
            if (isset($server['CONTENT_LENGTH'])) {
                $header['content-length'] = $server['CONTENT_LENGTH'];
            }
        }
        
        $request->header = array_change_key_case($header);
        $request->server = $_SERVER;
        $request->env    = $app->env;
        
        $inputData = $request->getInputData($request->input);
        
        $request->get     = $_GET;
        $request->post    = $_POST ?: $inputData;
        $request->put     = $inputData;
        $request->request = $_REQUEST;
        $request->cookie  = $_COOKIE;
        // $request->file    = $_FILES ?? [];
        self::$_uploadTmpDir = $app->getRuntimePath() . 'TempUpload';
        if (empty($request->file)) {
            $request->parseUploadFiles();
        }
        
        return $request;
    }
    
    
    /**
     * 获取上传的文件信息
     * @access public
     * @param  string $name 名称
     * @return null|array|UploadedFile
     */
    public function file(string $name = '')
    {
        $files = $this->file;
        if (!empty($files)) {
            if (strpos($name, '.')) {
                [$name, $sub] = explode('.', $name);
            }
            
            // 处理上传文件
            $array = $this->dealUploadFile($files, $name);
            
            if ('' === $name) {
                // 获取全部文件
                return $array;
            } elseif (isset($sub) && isset($array[$name][$sub])) {
                return $array[$name][$sub];
            } elseif (isset($array[$name])) {
                return $array[$name];
            }
        }
    }
    
    protected function dealUploadFile(array $files, string $name): array
    {
        $array = [];
        foreach ($files as $key => $file) {
            if (is_array($file['name'])) {
                $item  = [];
                $keys  = array_keys($file);
                $count = count($file['name']);
                
                for ($i = 0; $i < $count; $i++) {
                    if ($file['error'][$i] > 0) {
                        if ($name == $key) {
                            $this->throwUploadFileError($file['error'][$i]);
                        } else {
                            continue;
                        }
                    }
                    
                    $temp['key'] = $key;
                    
                    foreach ($keys as $_key) {
                        $temp[$_key] = $file[$_key][$i];
                    }
                    
                    $item[] = new UploadedFile($temp['tmp_name'], $temp['name'], $temp['type'], $temp['error']);
                }
                
                $array[$key] = $item;
            } else {
                if ($file instanceof File) {
                    $array[$key] = $file;
                } else {
                    if ($file['error'] > 0) {
                        if ($key == $name) {
                            $this->throwUploadFileError($file['error']);
                        } else {
                            continue;
                        }
                    }
                    
                    $array[$key] = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error']);
                }
            }
        }
        
        return $array;
    }
    
    /**
     * 解析文件上传
     * Parse upload files.
     *
     * @return void
     */
    protected function parseUploadFiles(): void
    {
        $files  =  $_FILES;
        foreach ($files as $file) {
            $this->file[$file['name']] = $this->parseUploadFile($file);
        }
    }
    
    /**
     * 解析文件上传
     * @param array $tempFile 文件信息
     *
     * @return array
     */
    protected function parseUploadFile(array $tempFile): array
    {
        $tmp_file = '';
        $error = UPLOAD_ERR_OK;
        $tmp_upload_dir = $this->uploadTmpDir();
        if (!$tmp_upload_dir) {
            $error = UPLOAD_ERR_NO_TMP_DIR;
        } else if (empty($tempFile['file_size'])) {
            $error = UPLOAD_ERR_NO_FILE;
        } else {
            $this->mkdir($tmp_upload_dir);
            $tmp_file = \tempnam($tmp_upload_dir, 'workermanTp6.upload.');
            if ($tmp_file === false || !\file_put_contents($tmp_file, $tempFile['file_data'])) {
                $error = UPLOAD_ERR_CANT_WRITE;
            }
        }
    
        // Parse upload files.
        $file = [
            'name' => $tempFile['file_name'] ?? '',
            'tmp_name' => $tmp_file,
            'size' => $tempFile['file_size'] ?? '',
            'error' => $error,
            'type' => $tempFile['file_type'] ?? '',
        ];
        
        return $file;
    }
    
    /**
     * 创建目录
     *
     * @param string $directory
     *
     * @return void
     */
    protected function mkdir(string $directory): void
    {
        if (!is_dir($directory)) {
            if (false === @mkdir($directory, 0777, true) && !is_dir($directory)) {
                throw new FileException(sprintf('Unable to create the "%s" directory', $directory));
            }
        } elseif (!is_writable($directory)) {
            throw new FileException(sprintf('Unable to write in the "%s" directory', $directory));
        }
    }
    
    /**
     * 文件上传临时目录
     * Set or get uploadTmpDir.
     *
     * @return bool|string
     */
    public static function uploadTmpDir($dir = null)
    {
        if (null !== $dir) {
            static::$_uploadTmpDir = $dir;
        }
        if (static::$_uploadTmpDir === '') {
            if ($upload_tmp_dir = \ini_get('upload_tmp_dir')) {
                static::$_uploadTmpDir = $upload_tmp_dir;
            } else if ($upload_tmp_dir = \sys_get_temp_dir()) {
                static::$_uploadTmpDir = $upload_tmp_dir;
            }
        }
        return static::$_uploadTmpDir;
    }
    
    /**
     * 文件上传临时目录
     * Set or get uploadTmpDir.
     *
     * @return bool|string
     */
    public static function fileUploads($dir = null)
    {
        if (null !== $dir) {
            static::$_fileUploads = $dir;
        }
        if (static::$_fileUploads === '') {
            if ($upload_tmp_dir = \ini_get('file_uploads')) {
                static::$_uploadTmpDir = $upload_tmp_dir;
            }
        }
        return static::$_uploadTmpDir;
    }
    
    public function destroyTempUploadedFiles()
    {
        if (!empty($this->file)) {
            \clearstatcache();
            foreach($this->file as $key => $value){
                $file = $value['tmp_name'] ?? '';
                if (isset($file)) {
                    if (\is_file($file)) {
                        \unlink($file);
                    }
                }
                unset($this->file[$key]);
            }
        }
    }
    
}
